/**
 * tftpserver.h
 * TFTP Server Funcions.
 * By Group 1
 */
#include "tftpserver.h"
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>

using namespace std;


void TFTPwriteFile(PolonetConn conn, const char* fileName, const char* mode)
{
    
    const char* barra = strchr(fileName,'/');

    if( barra != NULL  )
    {
        string errmsg = "File access violation, no directories are allowed.";
        transmitERRORPacket(conn,ERROR_ACCESSVIOLATION,errmsg.c_str());
        cout << "ERROR: " << errmsg << endl;
    }
    else
    {
        FILE* fileToReceive = fopen(fileName,"rb");
        if(fileToReceive != NULL)
        {
            string errmsg = "File already exists.";
            transmitERRORPacket(conn,ERROR_FILEALREADYEXISTS,errmsg.c_str());
            cout << "ERROR: " << errmsg << endl;
            fclose(fileToReceive);
        }
        else
        {
            cout << "Starting reception of file: " << fileName << endl;
            transmitACKPacket(conn,0);
            char* receptionBuffer = (char*) malloc(sizeof(char)*BUFFER_SIZE);
            char* auxBuffer = (char*) malloc(sizeof(char)*512);
            bool transferOK = true;
            bool transferDone = false;
            bool connected = true;
            short blockNumber;
            int receivedBytes = loadBuffer(conn, receptionBuffer, BUFFER_SIZE);
            int packetCount = 1;

            if(getPacketType(receptionBuffer)==DATA)
            {
                fileToReceive = fopen(fileName,"wb");
                if (fileToReceive != NULL)
                {
                    while (transferOK && transferDone == false && connected)
                    {
                        switch (getPacketType(receptionBuffer))
                        {
                        case DATA:
                            if (readDATAPacket(auxBuffer, blockNumber, receptionBuffer,receivedBytes))
                            {
                                cout << "\r" << packetCount << " packets received...";
                                cout.flush();

                                fwrite(auxBuffer,sizeof(char),receivedBytes-4,fileToReceive),
                                transmitACKPacket(conn,blockNumber);
                                if (receivedBytes < 516)
                                    transferDone = true;
                                else
                                {
                                    receivedBytes = loadBuffer(conn, receptionBuffer, BUFFER_SIZE);
                                    packetCount++;
                                }
                            }
                            else
                            {
                                cout << "Packet got corrupted during execution, disconnecting client..." << endl;
                                transferOK = false;
                            }
                            break;
                        default:
                            cout << "An error has ocurred with the transmission, aborting..." << endl;
                            transferOK = false;
                            string errmsg = "Unexpected packet.";
                            transmitERRORPacket(conn,ERROR_ILLEGALOPERATION, errmsg.c_str());
                            cout << "ERROR: " << errmsg << endl;
                        }
                        if (!isConnected(conn))
                        {
                            connected = false;
                        }
                    }

                    if(transferOK)
                        cout << "\r" << packetCount << " packets have been received from the client..." << endl;

                    free(auxBuffer);
                    free(receptionBuffer);
                    fclose(fileToReceive);

                }
                else
                {
                    cout << "Error opening file: " << fileName << endl;
                    string errmsg = "File could not be opened.";
                    transmitERRORPacket(conn,ERROR_DISKFULL,errmsg.c_str());
                    cout << "ERROR: " << errmsg << endl;
                }
            }
            else
            {
                cout << "An unknown error has ocurred, aborting transmission..." << endl;
            }



        }
    }
}

void TFTPreadFile(PolonetConn conn, const char* fileName, const char* mode)
{

    const char* barra = strchr(fileName,'/');

    if( barra != NULL )
    {
        string errmsg = "File access violation, no directories are allowed.";
        transmitERRORPacket(conn,ERROR_ACCESSVIOLATION,errmsg.c_str());
        cout << "ERROR: " << errmsg << endl;
    }
    else
    {
        FILE* fileToSend = fopen(fileName,"rb");
        if(fileToSend != NULL)
        {
            cout << "Starting transmission of file: " << fileName << endl;
            bool transferOK = true;
            short counter = 1;
            short receptionCounter;
            char* receptionBuffer = (char*) malloc(sizeof(char)*BUFFER_SIZE);
            char* auxBuffer = (char*) malloc(sizeof(char)*512);
            bool connected = true;

            int packetCount = 0;

            while(feof(fileToSend) == false && transferOK && connected)
            {
                int readCount = fread(auxBuffer,sizeof(char),512,fileToSend);
                transmitDATAPacket(conn,counter,auxBuffer,readCount);

                cout << "\r" << packetCount << " packets transmitted...";
                cout.flush();

                loadBuffer(conn, receptionBuffer, BUFFER_SIZE);
                switch (getPacketType(receptionBuffer))
                {
                case ACK:
                    if (readACKPacket(receptionCounter, receptionBuffer))
                    {
                        if (receptionCounter != counter)
                        {
                            cout << "An error has ocurred with the transmission, aborting..." << endl;
                            transferOK = false;
                            string errmsg = "Wrong packet number returned.";
                            transmitERRORPacket(conn,ERROR_UNKNOWNTRANSFERID,errmsg.c_str());
                        }
                        else
                            packetCount++;
                    }
                    else
                    {
                        cout << "Packet got corrupted during execution, disconnecting client..." << endl;
                        transferOK = false;
                    }
                    break;
                default:
                    cout << "An error has ocurred with the transmission, aborting..." << endl;
                    transferOK = false;
                    string errmsg = "Unexpected packet.";
                    transmitERRORPacket(conn,ERROR_ILLEGALOPERATION, errmsg.c_str());
                    cout << "ERROR: " << errmsg << endl;
                }
                counter++;
                if (!isConnected(conn))
                {
                    connected = false;
                }
            }
            if(transferOK)
                cout << "\r" << packetCount << " packets have been transfered to the client..." << endl;

            free(auxBuffer);
            free(receptionBuffer);
            fclose(fileToSend);
        }
        else
        {
            string errmsg = "File could not be opened or does not exist.";
            transmitERRORPacket(conn,ERROR_FILENOTFOUND,errmsg.c_str());
            cout << "ERROR: " << errmsg << endl;
        }
        
    }
}

void enterServerMainLoop()
{
    bool keepLoop = true;

    /* Well-behaved servers keep listening */
    while (keepLoop)
    {
        PolonetConn conn;
        char* buffer = (char *) malloc(sizeof(char)*BUFFER_SIZE);

        /* Wait for an incoming connection.
           If there is no connection, wait 10 milliseconds */
        while (!(conn = getAvailableConnection()))
            usleep(SLEEP);

        cout << "Connection establed with client, expecting data..." << endl;

        int loadedBytes = loadBuffer(conn,buffer,BUFFER_SIZE);

        if (loadedBytes >= 2)
        {
            char* filename;
            char* mode;
            switch (getPacketType(buffer))
            {
            case RRQ:
                if (readRRQWRQPacket(filename,mode,buffer))
                {
                    TFTPreadFile(conn,filename,mode);
                }
                else
                {
                    cout << "Packet got corrupted during execution, terminating server." << endl;
                    exit(1);
                }
                break;
            case WRQ:
                if (readRRQWRQPacket(filename,mode,buffer))
                {
                    TFTPwriteFile(conn,filename,mode);
                }
                else
                {
                    cout << "Packet got corrupted during execution, terminating server." << endl;
                    exit(1);
                }
                break;
                break;
            case ECHO:
                char* message;
                if (readECHOPacket(message, buffer))
                {
                    cout << "ECHO message received: " << message << endl;
                    transmitECHOPacket(conn, message);
                }
                else
                {
                    cout << "Packet got corrupted during execution, terminating server." << endl;
                    exit(1);
                }
                break;
            case DIE:
                cout << "Server shutdown requested..." << endl;
                keepLoop = false;
                break;
            default:
                string errmsg = "Unexpected first packet.";
                transmitERRORPacket(conn,ERROR_ILLEGALOPERATION, errmsg.c_str());
                cout << "ERROR: " << errmsg << endl;
                break;
            }
        }

        /* Always close open connections */
        cout << "Client disconnected..." << endl << endl;
        closeConnection(conn);

        free(buffer);
    }

    cout << endl << "Leaving Server Main Loop..." << endl;
}
